;
(function(root, factory) {
	if (typeof define === 'function' && define.amd) { // AMD
		define([], factory);
	} else if (typeof exports === 'object') { // Node.js
		module.exports = factory();
	} else { // Browser
		root.zio = factory();
	}
}(this, function() {
	'use strict';
	var zio = {
		ws: {},
		url: 'localhost', //连接的url地址
		link_url: '', //拼接后的连接地址
		params: {}, //url地址后的参数
		lastConnectTime: -1, //最后一次连接成功的时间
		lastSendTime: -1, //最后一次发送消息的时间
		pingIntervalId: null, //心跳任务id
		heartbeatTimeout: 2000, // 心跳时间(ms)
		heartbeatSendInterval: 1000, //心跳发送间隔
		reconnIntervalId: null, //重连任务id
		reconnInterval: 3000, // 重连间隔(ms)
		binaryType: 'blob', // 数据格式('blob' or 'arraybuffer')
		handler: {},
		multicasterList: [],//广播器列表
		executableList: ['open','message','close','error','ping','reconn'],//可执行handler列表
	};

	/**
	 * 工具
	 * */
	zio.utils = {};
	zio.utils.assembleUrl = function(reconnect) {
		var params = {};
		if (reconnect) {
			params["reconnect"] = true;
		}
		Object.assign(params, zio.params);
		if (zio.url && JSON.stringify(params) != "{}") {
			var paramArray = [];
			Object.keys(params).sort().forEach(function(key) {
				if (params[key] instanceof Object) {
					params[key] = JSON.stringify(params[key])
				}
				var param = key + '=' + params[key]
				paramArray.push(param)
			});
			var encode_url = encodeURI(zio.url + '?' + paramArray.join('&'));
			zio.link_url = encodeURI(encode_url);
		} else {
			zio.link_url = zio.url;
		}
	};
	zio.utils.arrayRemove = function(a, b) {
		b = a.indexOf(b);
		if (-1 == b) return !1;
		a.splice(b, 1);
		return !0
	};
	zio.utils.getState = function(){
		 if(zio.ws && zio.ws.readyState && typeof zio.ws.readyState == 'number'){
			 return zio.ws.readyState;
		 }else{
			return 4;
		 }
	 };

	/**
	 * 事件
	 * */
	zio.Events = {};
	zio.Events.CONNECT = "connect";//发起连接
	zio.Events.OPEN = "open";//打开连接
	zio.Events.MESSAGE = "message";//接收消息
	zio.Events.CLOSE = "close";//连接关闭
	zio.Events.ERROR = "error";//连接错误
	zio.Events.SEND = "send";//发送消息
	zio.Events.PING = "ping";//发送心跳
	zio.Events.RECONN = "reconn";//重连

	/**
	 * 监听器
	 * */
	 zio.Listener = function(a,f,...types) {
	 	this.name = a;
		if(typeof f == 'function'){
			this.onEvent = f;
		};
		this.types=[...types];
	 };
	 zio.Listener.prototype.onEvent = function(e) {};

	/**
	 * 广播器
	 * */
	zio.Multicaster = function(a) {
		this.name = a;
		this.listenerList = []; //事件监听器列表
	};
	zio.Multicaster.prototype.addListener = function(l) {
		if(l instanceof zio.Listener){
			this.listenerList.push(l);
			return true;
		}else{
			return false;
		}
	};
	zio.Multicaster.prototype.removeListener = function(l) {
		return zio.utils.arrayRemove(this.listenerList, l)
	};
	zio.Multicaster.prototype.doStart = function() {};
	zio.Multicaster.prototype.doEnd = function() {};
	zio.Multicaster.prototype.multicastEvent = function(obj) {
		this.doStart();
		this.listenerList.forEach(l => {
			if(l instanceof zio.Listener && l.types.includes(obj._type)){
				l.onEvent(obj);
			}
		});
		this.doEnd();
	};
	
	/**
	 * 添加广播器
	 * */
	zio.addMulticaster = function(m) {
		if(m instanceof zio.Multicaster){
			this.multicasterList.push(m);
			return true;
		}else{
			return false;
		}
	};
	
	/**
	 * 移除广播器
	 * */
	zio.removeMulticaster = function(m) {
		return zio.utils.arrayRemove(this.multicasterList, m)
	};

	/**
	 * 触发事件
	 * */
	zio.fireEvent = function(obj) {
		if(zio.handler&&zio.handler[obj._type]&&zio.executableList.includes(obj._type)){
			zio.handler[obj._type].call(zio.handler, obj);
		}
		if(zio.multicasterList){
			zio.multicasterList.forEach(m => {
				if(m instanceof zio.Multicaster){
					m.multicastEvent(obj);
				}
			});
		}
	}

	/**
	 * 建立连接
	 * */
	zio.connect = function({
		url,
		params,
		heartbeatTimeout,
		reconnInterval,
		binaryType,
		handler,
		reconn,
	}) {
		if(zio.utils.getState()==1){
			return zio.ws;
		}
		url ? zio.url = url : '';
		params ? zio.params = params : '';
		heartbeatTimeout ? zio.heartbeatTimeout = heartbeatTimeout : '';
		reconnInterval ? zio.reconnInterval = reconnInterval : '';
		binaryType ? zio.binaryType = binaryType : '';
		handler ? zio.handler = handler : '';
		zio.utils.assembleUrl(reconn);
		zio.heartbeatSendInterval = zio.heartbeatTimeout / 2;

		try {
			zio.ws = new WebSocket(zio.link_url);
		} catch (err) {
			console.error("Error creating WS connection", err)
		}

		/**
		 * 连接建立事件
		 * */
		zio.ws.onopen = function(event) {
			zio.lastConnectTime = zio.lastSendTime = new Date().getTime();
			if (zio.reconnIntervalId) {
				clearInterval(zio.reconnIntervalId);
			}
			zio.fireEvent({
				_type: zio.Events.OPEN,
				_event: event
			});
			zio.pingIntervalId = setInterval(function() {
				zio.ping()
			}, zio.heartbeatSendInterval)
		}

		/**
		 * 接收消息事件
		 * */
		zio.ws.onmessage = function(event) {
			zio.lastSendTime = new Date().getTime();
			zio.fireEvent({
				_type: zio.Events.MESSAGE,
				_event: event
			});
		}

		/**
		 * 连接关闭事件
		 * */
		zio.ws.onclose = function(event) {
			console.log(zio.ws);
			clearInterval(zio.pingIntervalId); //clear send heartbeat task
			zio.reconn();
			zio.fireEvent({
				_type: zio.Events.CLOSE,
				_event: event
			});
		}

		/**
		 * 连接错误事件
		 * */
		zio.ws.onerror = function(event) {
			clearInterval(zio.pingIntervalId); //clear send heartbeat task
			zio.fireEvent({
				_type: zio.Events.ERROR,
				_event: event
			});
		}
		
		return zio.ws;
	};

	/**
	 * 重连
	 * */
	zio.reconn = function() {
		zio.reconnIntervalId = setTimeout(function() {
			zio.fireEvent({
				_type: zio.Events.RECONN
			});
			zio.connect({
				reconn: true
			})
		}, zio.reconnInterval)
	}

	/**
	 * 发送心跳
	 * */
	zio.ping = function() {
		var iv = new Date().getTime() - zio.lastSendTime;
		if ((zio.heartbeatSendInterval + iv) >= zio.heartbeatTimeout) {
			zio.fireEvent({
				_type: zio.Events.PING,
				_ws: zio.ws
			});
		}
	};

	/**
	 * 发送消息
	 * */
	zio.send = function(data) {
		zio.fireEvent({
			_type: zio.Events.SEND,
			_data: data
		});
		zio.ws.send(data);
	};
	
	return zio;
}))
